package com.tyk.bookstore.back.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tyk.bookstore.back.common.core.util.AssertUtil;
import com.tyk.bookstore.back.common.core.util.IdGenerator;
import com.tyk.bookstore.back.common.core.util.ObjUtil;
import com.tyk.bookstore.back.system.model.entity.MenuDo;
import com.tyk.bookstore.back.system.model.entity.PermissionDo;
import com.tyk.bookstore.back.system.model.entity.RoleDo;
import com.tyk.bookstore.back.system.model.query.RoleAddQuery;
import com.tyk.bookstore.back.system.model.query.RolePageQuery;
import com.tyk.bookstore.back.system.model.query.RoleUpdateQuery;
import com.tyk.bookstore.back.system.model.result.MenuResultEnum;
import com.tyk.bookstore.back.system.model.result.PermissionResultEnum;
import com.tyk.bookstore.back.system.model.result.RoleResultEnum;
import com.tyk.bookstore.back.system.model.type.RoleState;
import com.tyk.bookstore.back.system.model.vo.RolePageVo;
import com.tyk.bookstore.back.system.model.vo.RoleSuggestionVo;
import com.tyk.bookstore.back.system.model.vo.RoleVo;
import com.tyk.bookstore.back.system.repository.*;
import com.tyk.bookstore.back.system.service.RoleService;
import com.tyk.bookstore.back.system.util.MapstructSystem;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.List;

@Slf4j
@RequiredArgsConstructor
@Service
public class RoleServiceImpl implements RoleService {
    private final TransactionTemplate transactionTemplate;
    private final MapstructSystem mapstructSystem;
    private final RoleRepository roleRepository;
    private final PermissionRepository permissionRepository;
    private final MenuRepository menuRepository;
    private final RolePermissionRepository rolePermissionRepository;
    private final RoleMenuRepository roleMenuRepository;
    private final UserRoleRepository userRoleRepository;


    @Override
    public RolePageVo getPage(RolePageQuery query) {
        Page<RoleDo> page = new Page<>(query.getPage(), query.getLimit());
        LambdaQueryWrapper<RoleDo> w = buildPageQuery(query);
        roleRepository.selectPage(page, w);
        return mapstructSystem.toRolePageVo(page);
    }


    @Override
    public void addRole(RoleAddQuery query) {

        // 名称不重复
        AssertUtil.isFalse(
                roleRepository.exist1Eq(
                        RoleDo::getName, query.getName(), RoleDo.class
                ),
                RoleResultEnum.ROLE_NAME_ALREADY_EXISTS
        );

        // 角色字符串不重复
        AssertUtil.isFalse(
                roleRepository.exist1Eq(
                        RoleDo::getCode, query.getCode(), RoleDo.class
                ),
                RoleResultEnum.ROLE_CODE_ALREADY_EXISTS
        );

        // 权限列表存在
        AssertUtil.equal(
                permissionRepository.count1In(
                        PermissionDo::getId, query.getPermissionIdList(), PermissionDo.class
                ),
                (long) query.getPermissionIdList().size(),
                PermissionResultEnum.PERMISSION_NOT_FOUND
        );

        // 菜单列表存在
        AssertUtil.equal(
                menuRepository.count1In(
                        MenuDo::getId, query.getMenuIdList(), MenuDo.class
                ),
                (long) query.getMenuIdList().size(),
                MenuResultEnum.MENU_NOT_FOUND
        );

        // 插入角色信息
        transactionTemplate.executeWithoutResult(transactionStatus -> {
            RoleDo role = mapstructSystem.toRoleDo(query);
            role.setId(IdGenerator.id());
            roleRepository.insert(role);
            if (ObjUtil.notEmpty(query.getPermissionIdList())) {
                rolePermissionRepository.insertBatchRolePermission(role.getId(), query.getPermissionIdList());
            }
            if (ObjUtil.notEmpty(query.getMenuIdList())) {
                roleMenuRepository.insertBatchRoleMenu(role.getId(), query.getMenuIdList());
            }
        });

    }

    @Override
    public RoleVo getById(Long id) {
        RoleVo vo = roleRepository.getRoleById(id);
        AssertUtil.notNull(vo, RoleResultEnum.ROLE_NOT_FOUND);
        return vo;
    }

    @Override
    public void updateById(RoleUpdateQuery query) {

        // 角色存在
        AssertUtil.isTrue(
                roleRepository.exist1Eq(
                        RoleDo::getId, query.getId(), RoleDo.class
                ),
                RoleResultEnum.ROLE_NOT_FOUND
        );

        // 名称不重复
        AssertUtil.isFalse(
                roleRepository.exist1Eq1Ne(
                        RoleDo::getName, query.getName(),
                        RoleDo::getId, query.getId(),
                        RoleDo.class
                ),
                RoleResultEnum.ROLE_NAME_ALREADY_EXISTS
        );

        // 角色字符串不重复
        AssertUtil.isFalse(
                roleRepository.exist1Eq1Ne(
                        RoleDo::getCode, query.getCode(),
                        RoleDo::getId, query.getId(),
                        RoleDo.class
                ),
                RoleResultEnum.ROLE_CODE_ALREADY_EXISTS
        );

        // 权限列表存在
        AssertUtil.equal(
                permissionRepository.count1In(
                        PermissionDo::getId, query.getPermissionIdList(), PermissionDo.class
                ),
                (long) query.getPermissionIdList().size(),
                PermissionResultEnum.PERMISSION_NOT_FOUND
        );

        // 菜单列表存在
        AssertUtil.equal(
                menuRepository.count1In(
                        MenuDo::getId, query.getMenuIdList(), MenuDo.class
                ),
                (long) query.getMenuIdList().size(),
                MenuResultEnum.MENU_NOT_FOUND
        );

        // 更新角色信息
        RoleDo roleDo = mapstructSystem.toRoleDo(query);
        transactionTemplate.executeWithoutResult(status -> {
            roleRepository.updateById(roleDo);
            rolePermissionRepository.deleteByRoleId(query.getId());
            if (ObjUtil.notEmpty(query.getPermissionIdList())) {
                rolePermissionRepository.insertBatchRolePermission(query.getId(), query.getPermissionIdList());
            }
            roleMenuRepository.deleteByRoleId(query.getId());
            if (ObjUtil.notEmpty(query.getMenuIdList())) {
                roleMenuRepository.insertBatchRoleMenu(query.getId(), query.getMenuIdList());
            }
        });
    }

    @Override
    public void deleteById(Long id) {
        // 角色存在
        AssertUtil.isTrue(
                roleRepository.exist1Eq(
                        RoleDo::getId, id, RoleDo.class
                ),
                RoleResultEnum.ROLE_NOT_FOUND
        );
        // 删除角色信息
        transactionTemplate.executeWithoutResult(transactionStatus -> {
            roleRepository.deleteById(id);
            rolePermissionRepository.deleteByRoleId(id);
            roleMenuRepository.deleteByRoleId(id);
            userRoleRepository.deleteByRoleId(id);
        });
    }

    @Override
    public List<RoleSuggestionVo> getSuggestionList() {
        LambdaQueryWrapper<RoleDo> w = Wrappers.lambdaQuery(RoleDo.class);
        w.select(RoleDo::getId, RoleDo::getName).eq(RoleDo::getState, RoleState.ABLE);
        return mapstructSystem.toRoleSuggestionVo(roleRepository.selectList(w));
    }


    /**
     * 构建分页查询Wrapper
     *
     * @param query 参数
     * @return Wrapper
     */
    private LambdaQueryWrapper<RoleDo> buildPageQuery(RolePageQuery query) {

        LambdaQueryWrapper<RoleDo> w = Wrappers.lambdaQuery(RoleDo.class);

        w.like(ObjUtil.notBlank(query.getName()), RoleDo::getName, query.getName());

        w.like(ObjUtil.notBlank(query.getCode()), RoleDo::getCode, query.getCode());

        w.eq(query.getState() != null, RoleDo::getState, query.getState());

        w.ge(query.getStartTime() != null, RoleDo::getCreateTime, query.getStartTime());
        w.le(query.getEndTime() != null, RoleDo::getCreateTime, query.getEndTime());

        w.orderBy(true, false, RoleDo::getCreateTime);

        return w;

    }

}
